#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>

#include "pixman-private.h"

#include "pixman-combine.h"

/*
 * There are two ways of handling alpha -- either as a single unified value or
 * a separate value for each component, hence each macro must have two
 * versions.  The unified alpha version has a 'U' at the end of the name,
 * the component version has a 'C'.  Similarly, functions which deal with
 * this difference will have two versions using the same convention.
 */


/*
 * Combine src and mask
 */
FASTCALL static void
pixman_fbCombineMaskU (comp4_t *src, const comp4_t *mask, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t a = *(mask + i) >> A_SHIFT;
        comp4_t s = *(src + i);
        FbByteMul(s, a);
        *(src + i) = s;
    }
}

/*
 * All of the composing functions
 */

FASTCALL static void
fbCombineClear (comp4_t *dest, const comp4_t *src, int width)
{
    memset(dest, 0, width*sizeof(comp4_t));
}

FASTCALL static void
fbCombineSrcU (comp4_t *dest, const comp4_t *src, int width)
{
    memcpy(dest, src, width*sizeof(comp4_t));
}

/* if the Src is opaque, call fbCombineSrcU */
FASTCALL static void
fbCombineOverU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t ia = Alpha(~s);

        FbByteMulAdd(d, ia, s);
	*(dest + i) = d;
    }
}

/* if the Dst is opaque, this is a noop */
FASTCALL static void
fbCombineOverReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t ia = Alpha(~*(dest + i));
        FbByteMulAdd(s, ia, d);
	*(dest + i) = s;
    }
}

/* if the Dst is opaque, call fbCombineSrcU */
FASTCALL static void
fbCombineInU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t a = Alpha(*(dest + i));
        FbByteMul(s, a);
	*(dest + i) = s;
    }
}

/* if the Src is opaque, this is a noop */
FASTCALL static void
fbCombineInReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp4_t a = Alpha(*(src + i));
        FbByteMul(d, a);
	*(dest + i) = d;
    }
}

/* if the Dst is opaque, call fbCombineClear */
FASTCALL static void
fbCombineOutU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t a = Alpha(~*(dest + i));
        FbByteMul(s, a);
	*(dest + i) = s;
    }
}

/* if the Src is opaque, call fbCombineClear */
FASTCALL static void
fbCombineOutReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp4_t a = Alpha(~*(src + i));
        FbByteMul(d, a);
	*(dest + i) = d;
    }
}

/* if the Src is opaque, call fbCombineInU */
/* if the Dst is opaque, call fbCombineOverU */
/* if both the Src and Dst are opaque, call fbCombineSrcU */
FASTCALL static void
fbCombineAtopU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t dest_a = Alpha(d);
        comp4_t src_ia = Alpha(~s);

        FbByteAddMul(s, dest_a, d, src_ia);
	*(dest + i) = s;
    }
}

/* if the Src is opaque, call fbCombineOverReverseU */
/* if the Dst is opaque, call fbCombineInReverseU */
/* if both the Src and Dst are opaque, call fbCombineDstU */
FASTCALL static void
fbCombineAtopReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t src_a = Alpha(s);
        comp4_t dest_ia = Alpha(~d);

        FbByteAddMul(s, dest_ia, d, src_a);
	*(dest + i) = s;
    }
}

/* if the Src is opaque, call fbCombineOverU */
/* if the Dst is opaque, call fbCombineOverReverseU */
/* if both the Src and Dst are opaque, call fbCombineClear */
FASTCALL static void
fbCombineXorU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t src_ia = Alpha(~s);
        comp4_t dest_ia = Alpha(~d);

        FbByteAddMul(s, dest_ia, d, src_ia);
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineAddU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        FbByteAdd(d, s);
	*(dest + i) = d;
    }
}

/* if the Src is opaque, call fbCombineAddU */
/* if the Dst is opaque, call fbCombineAddU */
/* if both the Src and Dst are opaque, call fbCombineAddU */
FASTCALL static void
fbCombineSaturateU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp2_t sa, da;

        sa = s >> A_SHIFT;
        da = ~d >> A_SHIFT;
        if (sa > da)
        {
            sa = IntDiv(da, sa);
            FbByteMul(s, sa);
        };
        FbByteAdd(d, s);
	*(dest + i) = d;
    }
}


/*
 * All of the disjoint composing functions

 The four entries in the first column indicate what source contributions
 come from each of the four areas of the picture -- areas covered by neither
 A nor B, areas covered only by A, areas covered only by B and finally
 areas covered by both A and B.

 Disjoint			Conjoint
 Fa		Fb		Fa		Fb
 (0,0,0,0)	0		0		0		0
 (0,A,0,A)	1		0		1		0
 (0,0,B,B)	0		1		0		1
 (0,A,B,A)	1		min((1-a)/b,1)	1		max(1-a/b,0)
 (0,A,B,B)	min((1-b)/a,1)	1		max(1-b/a,0)	1
 (0,0,0,A)	max(1-(1-b)/a,0) 0		min(1,b/a)	0
 (0,0,0,B)	0		max(1-(1-a)/b,0) 0		min(a/b,1)
 (0,A,0,0)	min(1,(1-b)/a)	0		max(1-b/a,0)	0
 (0,0,B,0)	0		min(1,(1-a)/b)	0		max(1-a/b,0)
 (0,0,B,A)	max(1-(1-b)/a,0) min(1,(1-a)/b)	 min(1,b/a)	max(1-a/b,0)
 (0,A,0,B)	min(1,(1-b)/a)	max(1-(1-a)/b,0) max(1-b/a,0)	min(1,a/b)
 (0,A,B,0)	min(1,(1-b)/a)	min(1,(1-a)/b)	max(1-b/a,0)	max(1-a/b,0)

*/

#define CombineAOut 1
#define CombineAIn  2
#define CombineBOut 4
#define CombineBIn  8

#define CombineClear	0
#define CombineA	(CombineAOut|CombineAIn)
#define CombineB	(CombineBOut|CombineBIn)
#define CombineAOver	(CombineAOut|CombineBOut|CombineAIn)
#define CombineBOver	(CombineAOut|CombineBOut|CombineBIn)
#define CombineAAtop	(CombineBOut|CombineAIn)
#define CombineBAtop	(CombineAOut|CombineBIn)
#define CombineXor	(CombineAOut|CombineBOut)

/* portion covered by a but not b */
FASTCALL static comp1_t
fbCombineDisjointOutPart (comp1_t a, comp1_t b)
{
    /* min (1, (1-b) / a) */

    b = ~b;		    /* 1 - b */
    if (b >= a)		    /* 1 - b >= a -> (1-b)/a >= 1 */
	return MASK;	    /* 1 */
    return IntDiv(b,a);     /* (1-b) / a */
}

/* portion covered by both a and b */
FASTCALL static comp1_t
fbCombineDisjointInPart (comp1_t a, comp1_t b)
{
    /* max (1-(1-b)/a,0) */
    /*  = - min ((1-b)/a - 1, 0) */
    /*  = 1 - min (1, (1-b)/a) */

    b = ~b;		    /* 1 - b */
    if (b >= a)		    /* 1 - b >= a -> (1-b)/a >= 1 */
	return 0;	    /* 1 - 1 */
    return ~IntDiv(b,a);    /* 1 - (1-b) / a */
}

/* portion covered by a but not b */
FASTCALL static comp1_t
fbCombineConjointOutPart (comp1_t a, comp1_t b)
{
    /* max (1-b/a,0) */
    /* = 1-min(b/a,1) */

    /* min (1, (1-b) / a) */

    if (b >= a)		    /* b >= a -> b/a >= 1 */
	return 0x00;	    /* 0 */
    return ~IntDiv(b,a);    /* 1 - b/a */
}

/* portion covered by both a and b */
FASTCALL static comp1_t
fbCombineConjointInPart (comp1_t a, comp1_t b)
{
    /* min (1,b/a) */

    if (b >= a)		    /* b >= a -> b/a >= 1 */
	return MASK;	    /* 1 */
    return IntDiv(b,a);     /* b/a */
}

FASTCALL static void
fbCombineDisjointGeneralU (comp4_t *dest, const comp4_t *src, int width, comp1_t combine)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t m,n,o,p;
        comp2_t Fa, Fb, t, u, v;
        comp1_t sa = s >> A_SHIFT;
        comp1_t da = d >> A_SHIFT;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            Fa = fbCombineDisjointOutPart (sa, da);
            break;
        case CombineAIn:
            Fa = fbCombineDisjointInPart (sa, da);
            break;
        case CombineA:
            Fa = MASK;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            Fb = fbCombineDisjointOutPart (da, sa);
            break;
        case CombineBIn:
            Fb = fbCombineDisjointInPart (da, sa);
            break;
        case CombineB:
            Fb = MASK;
            break;
        }
        m = FbGen (s,d,0,Fa,Fb,t, u, v);
        n = FbGen (s,d,G_SHIFT,Fa,Fb,t, u, v);
        o = FbGen (s,d,R_SHIFT,Fa,Fb,t, u, v);
        p = FbGen (s,d,A_SHIFT,Fa,Fb,t, u, v);
        s = m|n|o|p;
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineDisjointOverU (comp4_t *dest, const comp4_t *src, int width)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp2_t a = s >> A_SHIFT;

        if (a != 0x00)
        {
            if (a != MASK)
            {
                comp4_t d = *(dest + i);
                a = fbCombineDisjointOutPart (d >> A_SHIFT, a);
                FbByteMulAdd(d, a, s);
                s = d;
            }
	    *(dest + i) = s;
        }
    }
}

FASTCALL static void
fbCombineDisjointInU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineAIn);
}

FASTCALL static void
fbCombineDisjointInReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineBIn);
}

FASTCALL static void
fbCombineDisjointOutU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineAOut);
}

FASTCALL static void
fbCombineDisjointOutReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineBOut);
}

FASTCALL static void
fbCombineDisjointAtopU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineAAtop);
}

FASTCALL static void
fbCombineDisjointAtopReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineBAtop);
}

FASTCALL static void
fbCombineDisjointXorU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineDisjointGeneralU (dest, src, width, CombineXor);
}

FASTCALL static void
fbCombineConjointGeneralU (comp4_t *dest, const comp4_t *src, int width, comp1_t combine)
{
    int i;
    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t d = *(dest + i);
        comp4_t m,n,o,p;
        comp2_t Fa, Fb, t, u, v;
        comp1_t sa = s >> A_SHIFT;
        comp1_t da = d >> A_SHIFT;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            Fa = fbCombineConjointOutPart (sa, da);
            break;
        case CombineAIn:
            Fa = fbCombineConjointInPart (sa, da);
            break;
        case CombineA:
            Fa = MASK;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            Fb = fbCombineConjointOutPart (da, sa);
            break;
        case CombineBIn:
            Fb = fbCombineConjointInPart (da, sa);
            break;
        case CombineB:
            Fb = MASK;
            break;
        }
        m = FbGen (s,d,0,Fa,Fb,t, u, v);
        n = FbGen (s,d,G_SHIFT,Fa,Fb,t, u, v);
        o = FbGen (s,d,R_SHIFT,Fa,Fb,t, u, v);
        p = FbGen (s,d,A_SHIFT,Fa,Fb,t, u, v);
        s = m|n|o|p;
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineConjointOverU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineAOver);
}


FASTCALL static void
fbCombineConjointOverReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineBOver);
}


FASTCALL static void
fbCombineConjointInU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineAIn);
}


FASTCALL static void
fbCombineConjointInReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineBIn);
}

FASTCALL static void
fbCombineConjointOutU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineAOut);
}

FASTCALL static void
fbCombineConjointOutReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineBOut);
}

FASTCALL static void
fbCombineConjointAtopU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineAAtop);
}

FASTCALL static void
fbCombineConjointAtopReverseU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineBAtop);
}

FASTCALL static void
fbCombineConjointXorU (comp4_t *dest, const comp4_t *src, int width)
{
    fbCombineConjointGeneralU (dest, src, width, CombineXor);
}

/********************************************************************************/
/*************************** Per Channel functions ******************************/
/********************************************************************************/

FASTCALL static void
fbCombineMaskC (comp4_t *src, comp4_t *mask)
{
    comp4_t a = *mask;

    comp4_t	x;
    comp2_t	xa;

    if (!a)
    {
	*(src) = 0;
	return;
    }

    x = *(src);
    if (a == ~0)
    {
	x = x >> A_SHIFT;
	x |= x << G_SHIFT;
	x |= x << R_SHIFT;
	*(mask) = x;
	return;
    }

    xa = x >> A_SHIFT;
    FbByteMulC(x, a);
    *(src) = x;
    FbByteMul(a, xa);
    *(mask) = a;
}

FASTCALL static void
fbCombineMaskValueC (comp4_t *src, const comp4_t *mask)
{
    comp4_t a = *mask;
    comp4_t	x;

    if (!a)
    {
	*(src) = 0;
	return;
    }

    if (a == ~0)
	return;

    x = *(src);
    FbByteMulC(x, a);
    *(src) =x;
}

FASTCALL static void
fbCombineMaskAlphaC (const comp4_t *src, comp4_t *mask)
{
    comp4_t a = *(mask);
    comp4_t	x;

    if (!a)
	return;

    x = *(src) >> A_SHIFT;
    if (x == MASK)
	return;
    if (a == ~0)
    {
	x = x >> A_SHIFT;
	x |= x << G_SHIFT;
	x |= x << R_SHIFT;
	*(mask) = x;
	return;
    }

    FbByteMul(a, x);
    *(mask) = a;
}



FASTCALL static void
fbCombineClearC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    memset(dest, 0, width*sizeof(comp4_t));
}

FASTCALL static void
fbCombineSrcC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
	comp4_t s = *(src + i);
	comp4_t m = *(mask + i);

	fbCombineMaskValueC (&s, &m);

	*(dest) = s;
    }
}

FASTCALL static void
fbCombineOverC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
	comp4_t s = *(src + i);
	comp4_t m = *(mask + i);
	comp4_t a;

	fbCombineMaskC (&s, &m);

	a = ~m;
        if (a != ~0)
        {
            if (a)
            {
                comp4_t d = *(dest + i);
                FbByteMulAddC(d, a, s);
                s = d;
            }
	    *(dest + i) = s;
        }
    }
}

FASTCALL static void
fbCombineOverReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp4_t a = ~d >> A_SHIFT;

        if (a)
        {
            comp4_t s = *(src + i);
	    comp4_t m = *(mask + i);

	    fbCombineMaskValueC (&s, &m);

            if (a != MASK)
            {
                FbByteMulAdd(s, a, d);
            }
	    *(dest + i) = s;
        }
    }
}

FASTCALL static void
fbCombineInC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp2_t a = d >> A_SHIFT;
        comp4_t s = 0;
        if (a)
        {
	    comp4_t m = *(mask + i);

	    s = *(src + i);
	    fbCombineMaskValueC (&s, &m);
            if (a != MASK)
            {
                FbByteMul(s, a);
            }
        }
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineInReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t m = *(mask + i);
        comp4_t a;

	fbCombineMaskAlphaC (&s, &m);

	a = m;
        if (a != ~0)
        {
            comp4_t d = 0;
            if (a)
            {
                d = *(dest + i);
                FbByteMulC(d, a);
            }
	    *(dest + i) = d;
        }
    }
}

FASTCALL static void
fbCombineOutC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp2_t a = ~d >> A_SHIFT;
        comp4_t s = 0;
        if (a)
        {
	    comp4_t m = *(mask + i);

	    s = *(src + i);
	    fbCombineMaskValueC (&s, &m);

            if (a != MASK)
            {
                FbByteMul(s, a);
            }
        }
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineOutReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
	comp4_t s = *(src + i);
	comp4_t m = *(mask + i);
	comp4_t a;

	fbCombineMaskAlphaC (&s, &m);

        a = ~m;
        if (a != ~0)
        {
            comp4_t d = 0;
            if (a)
            {
                d = *(dest + i);
                FbByteMulC(d, a);
            }
	    *(dest + i) = d;
        }
    }
}

FASTCALL static void
fbCombineAtopC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp4_t s = *(src + i);
        comp4_t m = *(mask + i);
        comp4_t ad;
        comp2_t as = d >> A_SHIFT;

	fbCombineMaskC (&s, &m);

        ad = ~m;

        FbByteAddMulC(d, ad, s, as);
	*(dest + i) = d;
    }
}

FASTCALL static void
fbCombineAtopReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {

        comp4_t d = *(dest + i);
        comp4_t s = *(src + i);
        comp4_t m = *(mask + i);
        comp4_t ad;
        comp2_t as = ~d >> A_SHIFT;

	fbCombineMaskC (&s, &m);

	ad = m;

        FbByteAddMulC(d, ad, s, as);
	*(dest + i) = d;
    }
}

FASTCALL static void
fbCombineXorC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t d = *(dest + i);
        comp4_t s = *(src + i);
        comp4_t m = *(mask + i);
        comp4_t ad;
        comp2_t as = ~d >> A_SHIFT;

	fbCombineMaskC (&s, &m);

	ad = ~m;

        FbByteAddMulC(d, ad, s, as);
	*(dest + i) = d;
    }
}

FASTCALL static void
fbCombineAddC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t s = *(src + i);
        comp4_t m = *(mask + i);
        comp4_t d = *(dest + i);

	fbCombineMaskValueC (&s, &m);

        FbByteAdd(d, s);
	*(dest + i) = d;
    }
}

FASTCALL static void
fbCombineSaturateC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t s, d;
        comp2_t sa, sr, sg, sb, da;
        comp2_t t, u, v;
        comp4_t m,n,o,p;

        d = *(dest + i);
        s = *(src + i);
	m = *(mask + i);

	fbCombineMaskC (&s, &m);

        sa = (m >> A_SHIFT);
        sr = (m >> R_SHIFT) & MASK;
        sg = (m >> G_SHIFT) & MASK;
        sb =  m             & MASK;
        da = ~d >> A_SHIFT;

        if (sb <= da)
            m = Add(s,d,0,t);
        else
            m = FbGen (s, d, 0, (da << G_SHIFT) / sb, MASK, t, u, v);

        if (sg <= da)
            n = Add(s,d,G_SHIFT,t);
        else
            n = FbGen (s, d, G_SHIFT, (da << G_SHIFT) / sg, MASK, t, u, v);

        if (sr <= da)
            o = Add(s,d,R_SHIFT,t);
        else
            o = FbGen (s, d, R_SHIFT, (da << G_SHIFT) / sr, MASK, t, u, v);

        if (sa <= da)
            p = Add(s,d,A_SHIFT,t);
        else
            p = FbGen (s, d, A_SHIFT, (da << G_SHIFT) / sa, MASK, t, u, v);

	*(dest + i) = m|n|o|p;
    }
}

FASTCALL static void
fbCombineDisjointGeneralC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width, comp1_t combine)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t s, d;
        comp4_t m,n,o,p;
        comp4_t Fa, Fb;
        comp2_t t, u, v;
        comp4_t sa;
        comp1_t da;

        s = *(src + i);
        m = *(mask + i);
        d = *(dest + i);
        da = d >> A_SHIFT;

	fbCombineMaskC (&s, &m);

	sa = m;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            m = (comp4_t)fbCombineDisjointOutPart ((comp1_t) (sa >> 0), da);
            n = (comp4_t)fbCombineDisjointOutPart ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
            o = (comp4_t)fbCombineDisjointOutPart ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
            p = (comp4_t)fbCombineDisjointOutPart ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
            Fa = m|n|o|p;
            break;
        case CombineAIn:
            m = (comp4_t)fbCombineDisjointInPart ((comp1_t) (sa >> 0), da);
            n = (comp4_t)fbCombineDisjointInPart ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
            o = (comp4_t)fbCombineDisjointInPart ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
            p = (comp4_t)fbCombineDisjointInPart ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
            Fa = m|n|o|p;
            break;
        case CombineA:
            Fa = ~0;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            m = (comp4_t)fbCombineDisjointOutPart (da, (comp1_t) (sa >> 0));
            n = (comp4_t)fbCombineDisjointOutPart (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
            o = (comp4_t)fbCombineDisjointOutPart (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
            p = (comp4_t)fbCombineDisjointOutPart (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
            Fb = m|n|o|p;
            break;
        case CombineBIn:
            m = (comp4_t)fbCombineDisjointInPart (da, (comp1_t) (sa >> 0));
            n = (comp4_t)fbCombineDisjointInPart (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
            o = (comp4_t)fbCombineDisjointInPart (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
            p = (comp4_t)fbCombineDisjointInPart (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
            Fb = m|n|o|p;
            break;
        case CombineB:
            Fb = ~0;
            break;
        }
        m = FbGen (s,d,0,GetComp(Fa,0),GetComp(Fb,0),t, u, v);
        n = FbGen (s,d,G_SHIFT,GetComp(Fa,G_SHIFT),GetComp(Fb,G_SHIFT),t, u, v);
        o = FbGen (s,d,R_SHIFT,GetComp(Fa,R_SHIFT),GetComp(Fb,R_SHIFT),t, u, v);
        p = FbGen (s,d,A_SHIFT,GetComp(Fa,A_SHIFT),GetComp(Fb,A_SHIFT),t, u, v);
        s = m|n|o|p;
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineDisjointOverC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineAOver);
}

FASTCALL static void
fbCombineDisjointInC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineAIn);
}

FASTCALL static void
fbCombineDisjointInReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineBIn);
}

FASTCALL static void
fbCombineDisjointOutC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineAOut);
}

FASTCALL static void
fbCombineDisjointOutReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineBOut);
}

FASTCALL static void
fbCombineDisjointAtopC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineAAtop);
}

FASTCALL static void
fbCombineDisjointAtopReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineBAtop);
}

FASTCALL static void
fbCombineDisjointXorC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineDisjointGeneralC (dest, src, mask, width, CombineXor);
}

FASTCALL static void
fbCombineConjointGeneralC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width, comp1_t combine)
{
    int i;

    for (i = 0; i < width; ++i) {
        comp4_t s, d;
        comp4_t m,n,o,p;
        comp4_t Fa, Fb;
        comp2_t t, u, v;
        comp4_t sa;
        comp1_t da;

        s = *(src + i);
        m = *(mask + i);
        d = *(dest + i);
        da = d >> A_SHIFT;

	fbCombineMaskC (&s, &m);

        sa = m;

        switch (combine & CombineA) {
        default:
            Fa = 0;
            break;
        case CombineAOut:
            m = (comp4_t)fbCombineConjointOutPart ((comp1_t) (sa >> 0), da);
            n = (comp4_t)fbCombineConjointOutPart ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
            o = (comp4_t)fbCombineConjointOutPart ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
            p = (comp4_t)fbCombineConjointOutPart ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
            Fa = m|n|o|p;
            break;
        case CombineAIn:
            m = (comp4_t)fbCombineConjointInPart ((comp1_t) (sa >> 0), da);
            n = (comp4_t)fbCombineConjointInPart ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
            o = (comp4_t)fbCombineConjointInPart ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
            p = (comp4_t)fbCombineConjointInPart ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
            Fa = m|n|o|p;
            break;
        case CombineA:
            Fa = ~0;
            break;
        }

        switch (combine & CombineB) {
        default:
            Fb = 0;
            break;
        case CombineBOut:
            m = (comp4_t)fbCombineConjointOutPart (da, (comp1_t) (sa >> 0));
            n = (comp4_t)fbCombineConjointOutPart (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
            o = (comp4_t)fbCombineConjointOutPart (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
            p = (comp4_t)fbCombineConjointOutPart (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
            Fb = m|n|o|p;
            break;
        case CombineBIn:
            m = (comp4_t)fbCombineConjointInPart (da, (comp1_t) (sa >> 0));
            n = (comp4_t)fbCombineConjointInPart (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
            o = (comp4_t)fbCombineConjointInPart (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
            p = (comp4_t)fbCombineConjointInPart (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
            Fb = m|n|o|p;
            break;
        case CombineB:
            Fb = ~0;
            break;
        }
        m = FbGen (s,d,0,GetComp(Fa,0),GetComp(Fb,0),t, u, v);
        n = FbGen (s,d,G_SHIFT,GetComp(Fa,G_SHIFT),GetComp(Fb,G_SHIFT),t, u, v);
        o = FbGen (s,d,R_SHIFT,GetComp(Fa,R_SHIFT),GetComp(Fb,R_SHIFT),t, u, v);
        p = FbGen (s,d,A_SHIFT,GetComp(Fa,A_SHIFT),GetComp(Fb,A_SHIFT),t, u, v);
        s = m|n|o|p;
	*(dest + i) = s;
    }
}

FASTCALL static void
fbCombineConjointOverC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineAOver);
}

FASTCALL static void
fbCombineConjointOverReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineBOver);
}

FASTCALL static void
fbCombineConjointInC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineAIn);
}

FASTCALL static void
fbCombineConjointInReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineBIn);
}

FASTCALL static void
fbCombineConjointOutC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineAOut);
}

FASTCALL static void
fbCombineConjointOutReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineBOut);
}

FASTCALL static void
fbCombineConjointAtopC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineAAtop);
}

FASTCALL static void
fbCombineConjointAtopReverseC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineBAtop);
}

FASTCALL static void
fbCombineConjointXorC (comp4_t *dest, comp4_t *src, comp4_t *mask, int width)
{
    fbCombineConjointGeneralC (dest, src, mask, width, CombineXor);
}

static const CombineFuncU pixman_fbCombineFuncU[] = {
    fbCombineClear,
    fbCombineSrcU,
    NULL, /* CombineDst */
    fbCombineOverU,
    fbCombineOverReverseU,
    fbCombineInU,
    fbCombineInReverseU,
    fbCombineOutU,
    fbCombineOutReverseU,
    fbCombineAtopU,
    fbCombineAtopReverseU,
    fbCombineXorU,
    fbCombineAddU,
    fbCombineSaturateU,
    NULL,
    NULL,
    fbCombineClear,
    fbCombineSrcU,
    NULL, /* CombineDst */
    fbCombineDisjointOverU,
    fbCombineSaturateU, /* DisjointOverReverse */
    fbCombineDisjointInU,
    fbCombineDisjointInReverseU,
    fbCombineDisjointOutU,
    fbCombineDisjointOutReverseU,
    fbCombineDisjointAtopU,
    fbCombineDisjointAtopReverseU,
    fbCombineDisjointXorU,
    NULL,
    NULL,
    NULL,
    NULL,
    fbCombineClear,
    fbCombineSrcU,
    NULL, /* CombineDst */
    fbCombineConjointOverU,
    fbCombineConjointOverReverseU,
    fbCombineConjointInU,
    fbCombineConjointInReverseU,
    fbCombineConjointOutU,
    fbCombineConjointOutReverseU,
    fbCombineConjointAtopU,
    fbCombineConjointAtopReverseU,
    fbCombineConjointXorU,
};

static const CombineFuncC pixman_fbCombineFuncC[] = {
    fbCombineClearC,
    fbCombineSrcC,
    NULL, /* Dest */
    fbCombineOverC,
    fbCombineOverReverseC,
    fbCombineInC,
    fbCombineInReverseC,
    fbCombineOutC,
    fbCombineOutReverseC,
    fbCombineAtopC,
    fbCombineAtopReverseC,
    fbCombineXorC,
    fbCombineAddC,
    fbCombineSaturateC,
    NULL,
    NULL,
    fbCombineClearC,	    /* 0x10 */
    fbCombineSrcC,
    NULL, /* Dest */
    fbCombineDisjointOverC,
    fbCombineSaturateC, /* DisjointOverReverse */
    fbCombineDisjointInC,
    fbCombineDisjointInReverseC,
    fbCombineDisjointOutC,
    fbCombineDisjointOutReverseC,
    fbCombineDisjointAtopC,
    fbCombineDisjointAtopReverseC,
    fbCombineDisjointXorC,  /* 0x1b */
    NULL,
    NULL,
    NULL,
    NULL,
    fbCombineClearC,
    fbCombineSrcC,
    NULL, /* Dest */
    fbCombineConjointOverC,
    fbCombineConjointOverReverseC,
    fbCombineConjointInC,
    fbCombineConjointInReverseC,
    fbCombineConjointOutC,
    fbCombineConjointOutReverseC,
    fbCombineConjointAtopC,
    fbCombineConjointAtopReverseC,
    fbCombineConjointXorC,
};

const FbComposeFunctions pixman_composeFunctions = {
    pixman_fbCombineFuncU,
    pixman_fbCombineFuncC,
    pixman_fbCombineMaskU
};
